bootloader: Add a zipl bootloader backend
authorColin Walters <walters@verbum.org>
Mon, 14 Oct 2019 19:22:19 +0000 (19:22 +0000)
committerColin Walters <walters@verbum.org>
Wed, 16 Oct 2019 14:22:02 +0000 (14:22 +0000)
zipl is a bit special in that it parses the BLS config files
directly *but* we need to run the command to update the "boot block".

Hence, we're not generating a separate config file like the other
backends.  Instead, extend the bootloader interface with a `post_bls_sync`
method that is run in the same place we swap the `boot/loader` symlink.

We write a "stamp file" in `/boot` that says we need to run this command.
The reason we use stamp file is to prevent the case where the system is
interrupted after BLS file is updated, but before zipl is triggered,
then zipl boot records are not updated.
This opens the door to making things eventually-consistent/reconcilable
by later adding a systemd unit to run `zipl` if we're interrupted via
a systemd unit - I think we should eventually take this approach
everywhere rather than requiring `/boot/loader` to be a symlink.

Author: Colin Walters <walters@verbum.org>
Tested-by: Tuan Hoang <tmhoang@linux.ibm.com>
Co-Authored-By: Tuan Hoang <tmhoang@linux.ibm.com>
Makefile-libostree.am
src/libostree/ostree-bootloader-zipl.c [new file with mode: 0644]
src/libostree/ostree-bootloader-zipl.h [new file with mode: 0644]
src/libostree/ostree-bootloader.c
src/libostree/ostree-bootloader.h
src/libostree/ostree-repo.c
src/libostree/ostree-sysroot-deploy.c

index 46984a75e1b33f81bf13107cb41b30f11fd4bd30..4b412fec970b25a6e07d3597ab47f4a398c4bf3c 100644 (file)
@@ -117,6 +117,8 @@ libostree_1_la_SOURCES = \
        src/libostree/ostree-bootloader.c \
        src/libostree/ostree-bootloader-grub2.h \
        src/libostree/ostree-bootloader-grub2.c \
+       src/libostree/ostree-bootloader-zipl.h \
+       src/libostree/ostree-bootloader-zipl.c \
        src/libostree/ostree-bootloader-syslinux.h \
        src/libostree/ostree-bootloader-syslinux.c \
        src/libostree/ostree-bootloader-uboot.h \
diff --git a/src/libostree/ostree-bootloader-zipl.c b/src/libostree/ostree-bootloader-zipl.c
new file mode 100644 (file)
index 0000000..4ac785d
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2019 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "ostree-sysroot-private.h"
+#include "ostree-bootloader-zipl.h"
+#include "otutil.h"
+
+#include <string.h>
+
+/* This is specific to zipl today, but in the future we could also
+ * use it for the grub2-mkconfig case.
+ */
+static const char zipl_requires_execute_path[] = "boot/ostree-bootloader-update.stamp";
+
+struct _OstreeBootloaderZipl
+{
+  GObject       parent_instance;
+
+  OstreeSysroot  *sysroot;
+};
+
+typedef GObjectClass OstreeBootloaderZiplClass;
+
+static void _ostree_bootloader_zipl_bootloader_iface_init (OstreeBootloaderInterface *iface);
+G_DEFINE_TYPE_WITH_CODE (OstreeBootloaderZipl, _ostree_bootloader_zipl, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (OSTREE_TYPE_BOOTLOADER, _ostree_bootloader_zipl_bootloader_iface_init));
+
+static gboolean
+_ostree_bootloader_zipl_query (OstreeBootloader *bootloader,
+                                   gboolean         *out_is_active,
+                                   GCancellable     *cancellable,
+                                   GError          **error)
+{
+  /* We don't auto-detect this one; should be explicitly chosen right now.
+   * see also https://github.com/coreos/coreos-assembler/pull/849
+   */
+  *out_is_active = FALSE;
+  return TRUE;
+}
+
+static const char *
+_ostree_bootloader_zipl_get_name (OstreeBootloader *bootloader)
+{
+  return "zipl";
+}
+
+static gboolean
+_ostree_bootloader_zipl_write_config (OstreeBootloader  *bootloader,
+                                          int                bootversion,
+                                          GPtrArray         *new_deployments,
+                                          GCancellable      *cancellable,
+                                          GError           **error)
+{
+  OstreeBootloaderZipl *self = OSTREE_BOOTLOADER_ZIPL (bootloader);
+
+  /* Write our stamp file */
+  if (!glnx_file_replace_contents_at (self->sysroot->sysroot_fd, zipl_requires_execute_path,
+                                      (guint8*)"", 0, GLNX_FILE_REPLACE_NODATASYNC,
+                                      cancellable, error))
+    return FALSE;
+
+  return TRUE;
+}
+
+static gboolean
+_ostree_bootloader_zipl_post_bls_sync (OstreeBootloader  *bootloader,
+                                       GCancellable  *cancellable,
+                                       GError       **error)
+{
+  OstreeBootloaderZipl *self = OSTREE_BOOTLOADER_ZIPL (bootloader);
+
+  /* Note that unlike the grub2-mkconfig backend, we make no attempt to
+   * chroot().
+   */
+  g_assert (self->sysroot->booted_deployment);
+
+  if (!glnx_fstatat_allow_noent (self->sysroot->sysroot_fd, zipl_requires_execute_path, NULL, 0, error))
+    return FALSE;
+
+  /* If there's no stamp file, nothing to do */
+  if (errno == ENOENT)
+    return TRUE;
+
+  const char *const zipl_argv[] = {"zipl", NULL};
+  int estatus;
+  if (!g_spawn_sync (NULL, (char**)zipl_argv, NULL, G_SPAWN_SEARCH_PATH,
+                     NULL, NULL, NULL, NULL, &estatus, error))
+    return FALSE;
+  if (!g_spawn_check_exit_status (estatus, error))
+    return FALSE;
+  if (!glnx_unlinkat (self->sysroot->sysroot_fd, zipl_requires_execute_path, 0, error))
+    return FALSE;
+  return TRUE;
+}
+
+static void
+_ostree_bootloader_zipl_finalize (GObject *object)
+{
+  OstreeBootloaderZipl *self = OSTREE_BOOTLOADER_ZIPL (object);
+
+  g_clear_object (&self->sysroot);
+
+  G_OBJECT_CLASS (_ostree_bootloader_zipl_parent_class)->finalize (object);
+}
+
+void
+_ostree_bootloader_zipl_init (OstreeBootloaderZipl *self)
+{
+}
+
+static void
+_ostree_bootloader_zipl_bootloader_iface_init (OstreeBootloaderInterface *iface)
+{
+  iface->query = _ostree_bootloader_zipl_query;
+  iface->get_name = _ostree_bootloader_zipl_get_name;
+  iface->write_config = _ostree_bootloader_zipl_write_config;
+  iface->post_bls_sync = _ostree_bootloader_zipl_post_bls_sync;
+}
+
+void
+_ostree_bootloader_zipl_class_init (OstreeBootloaderZiplClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  object_class->finalize = _ostree_bootloader_zipl_finalize;
+}
+
+OstreeBootloaderZipl *
+_ostree_bootloader_zipl_new (OstreeSysroot *sysroot)
+{
+  OstreeBootloaderZipl *self = g_object_new (OSTREE_TYPE_BOOTLOADER_ZIPL, NULL);
+  self->sysroot = g_object_ref (sysroot);
+  return self;
+}
diff --git a/src/libostree/ostree-bootloader-zipl.h b/src/libostree/ostree-bootloader-zipl.h
new file mode 100644 (file)
index 0000000..79e8491
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#pragma once
+
+#include "ostree-bootloader.h"
+
+G_BEGIN_DECLS
+
+#define OSTREE_TYPE_BOOTLOADER_ZIPL (_ostree_bootloader_zipl_get_type ())
+#define OSTREE_BOOTLOADER_ZIPL(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), OSTREE_TYPE_BOOTLOADER_ZIPL, OstreeBootloaderZipl))
+#define OSTREE_IS_BOOTLOADER_ZIPL(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), OSTREE_TYPE_BOOTLOADER_ZIPL))
+
+typedef struct _OstreeBootloaderZipl OstreeBootloaderZipl;
+
+GType _ostree_bootloader_zipl_get_type (void) G_GNUC_CONST;
+
+OstreeBootloaderZipl * _ostree_bootloader_zipl_new (OstreeSysroot *sysroot);
+
+G_END_DECLS
index 5b8125dcc45d2c522a9b0bbf2edba08866027c5f..76b7bb82013d814783ee32b9f1cf737589b1ba6f 100644 (file)
@@ -65,6 +65,19 @@ _ostree_bootloader_write_config (OstreeBootloader  *self,
                                                            cancellable, error);
 }
 
+gboolean
+_ostree_bootloader_post_bls_sync (OstreeBootloader  *self,
+                                  GCancellable  *cancellable,
+                                  GError       **error)
+{
+  g_return_val_if_fail (OSTREE_IS_BOOTLOADER (self), FALSE);
+
+  if (OSTREE_BOOTLOADER_GET_IFACE (self)->post_bls_sync)
+    return OSTREE_BOOTLOADER_GET_IFACE (self)->post_bls_sync (self, cancellable, error);
+
+  return TRUE;
+}
+
 gboolean
 _ostree_bootloader_is_atomic (OstreeBootloader  *self)
 {
index 5af2dcc8a3d90252ca0c5459aea489c3a321fc6a..48a7a9cdaa32a0969dff7aed06ea19707f574c19 100644 (file)
@@ -47,6 +47,9 @@ struct _OstreeBootloaderInterface
                                                    GPtrArray     *new_deployments,
                                                    GCancellable  *cancellable,
                                                    GError       **error);
+  gboolean             (* post_bls_sync)          (OstreeBootloader  *self,
+                                                   GCancellable  *cancellable,
+                                                   GError       **error);
   gboolean             (* is_atomic)              (OstreeBootloader  *self);
 };
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeBootloader, g_object_unref)
@@ -66,6 +69,10 @@ gboolean _ostree_bootloader_write_config (OstreeBootloader  *self,
                                           GCancellable  *cancellable,
                                           GError       **error);
 
+gboolean _ostree_bootloader_post_bls_sync (OstreeBootloader  *self,
+                                           GCancellable  *cancellable,
+                                           GError       **error);
+
 gboolean _ostree_bootloader_is_atomic (OstreeBootloader  *self);
 
 G_END_DECLS
index b654aff2a0b833511d8f4029b11dc2ff6da54e70..e86489d3685cb9e736f66e8bb46e8b30281deeb9 100644 (file)
@@ -3184,8 +3184,10 @@ reload_sysroot_config (OstreeRepo          *self,
      * binary "x" in /usr/lib/ostree/bootloaders/x). See:
      * https://github.com/ostreedev/ostree/issues/1719
      * https://github.com/ostreedev/ostree/issues/1801
+     * Also, dedup these strings with the bootloader implementations
      */
-    if (!(g_str_equal (bootloader, "auto") || g_str_equal (bootloader, "none")))
+    if (!(g_str_equal (bootloader, "auto") || g_str_equal (bootloader, "none")
+          || g_str_equal (bootloader, "zipl")))
       return glnx_throw (error, "Invalid bootloader configuration: '%s'", bootloader);
 
     g_free (self->bootloader);
index c342d7e0042e21d42ee038a43ebc32ffdbb19462..cc0b5c99b318b596a867cf99834429f33281e94f 100644 (file)
@@ -43,6 +43,7 @@
 #include "ostree.h"
 #include "ostree-sysroot-private.h"
 #include "ostree-sepolicy-private.h"
+#include "ostree-bootloader-zipl.h"
 #include "ostree-deployment-private.h"
 #include "ostree-core-private.h"
 #include "ostree-linuxfsutil.h"
@@ -1830,6 +1831,7 @@ prepare_new_bootloader_link (OstreeSysroot  *sysroot,
 /* Update the /boot/loader symlink to point to /boot/loader.$new_bootversion */
 static gboolean
 swap_bootloader (OstreeSysroot  *sysroot,
+                 OstreeBootloader *bootloader,
                  int             current_bootversion,
                  int             new_bootversion,
                  GCancellable   *cancellable,
@@ -1863,6 +1865,15 @@ swap_bootloader (OstreeSysroot  *sysroot,
   if (fsync (boot_dfd) != 0)
     return glnx_throw_errno_prefix (error, "fsync(boot)");
 
+  /* TODO: In the future also execute this automatically via a systemd unit
+   * if we detect it's necessary.
+   **/
+  if (bootloader)
+    {
+      if (!_ostree_bootloader_post_bls_sync (bootloader, cancellable, error))
+        return FALSE;
+    }
+
   return TRUE;
 }
 
@@ -2129,7 +2140,7 @@ write_deployments_bootswap (OstreeSysroot     *self,
   if (!full_system_sync (self, out_syncstats, cancellable, error))
     return FALSE;
 
-  if (!swap_bootloader (self, self->bootversion, new_bootversion,
+  if (!swap_bootloader (self, bootloader, self->bootversion, new_bootversion,
                         cancellable, error))
     return FALSE;
 
@@ -2356,6 +2367,14 @@ ostree_sysroot_write_deployments_with_options (OstreeSysroot     *self,
         {
           /* No bootloader specified; do not query bootloaders to run. */
         }
+      else if (g_str_equal (bootloader_config, "zipl"))
+        {
+          /* Because we do not mark zipl as active by default, lets creating one here,
+           * which is basically the same what _ostree_sysroot_query_bootloader() does
+           * for other bootloaders if being activated.
+           * */
+          bootloader = (OstreeBootloader*) _ostree_bootloader_zipl_new (self);
+        }
 
       bootloader_is_atomic = bootloader != NULL && _ostree_bootloader_is_atomic (bootloader);